package cn.com.colasoft.netty.helloworld;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler;
import io.netty.handler.traffic.GlobalTrafficShapingHandler;

import java.util.concurrent.Executors;

/**
 * Created by lizhitao on 2017/6/7.
 */
public class Server {
    public static void main(String[] args) throws InterruptedException {
        // 第一个线程组,用于接收client端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        // 第二个线程组,用于实际处理业务逻辑
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        // 创建一个辅助类,就是对我们的server端进行一系列的配置
        ServerBootstrap b = new ServerBootstrap();
        // 加入以上两个线程组
        b.group(bossGroup, workerGroup)
                // 指定需要使用哪种类型的通道,如果是处理http的需要选用http相应的通道
                .channel(NioServerSocketChannel.class)
                //设置日志
                .handler(new LoggingHandler(LogLevel.INFO))
                // 一定要使用childHandler去绑定具体的事件处理器
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        // 设置特殊字符分隔符,解决拆包\粘包的问题
                        ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
                        sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
                        // 设置字符串形式的解码
                        sc.pipeline().addLast(new StringDecoder());
                        sc.pipeline().addLast(new ServerHandler());
                    }
                })
                /**
                 * 服务器端TCP内核模块维护了两个队列,我们称之为A \ B 吧.
                 * 客户端向服务器端connect的时候,会发送带有SYN标志的包(第一次握手).
                 * 服务器端收到客户端发来的SYN时,向客户端发送SYN ACK确认(第二次握手).
                 * 此时TCP内核模块把客户端连接加入到A队列中,然后服务器收到客户端发送来得ACK时(第三次握手),TCP内核模块把客户端连接从A队列
                 * 移动到B队列,连接完成.应用程序的accept回返回.也就是说accept从B队列中取出完成三次握手的连接.
                 * A队列和B队列的长度之和是blacklog,当A,B队列的长度之和大于backlog时,新连接将会被TCP内核拒绝,
                 * 所以,如果backlog过小,可能会出现accept速度跟不上,A,B队列满了,导致新的客户端无法连接,更要注意的是,backlog对程序支持
                 * 的连接数并无影响,backlog影响的只是还没有被accept取出的连接
                 */
                .option(ChannelOption.SO_BACKLOG, 128) // tcp连接缓冲队列大小,默认128
                .option(ChannelOption.SO_KEEPALIVE, true)//保持连接
                .option(ChannelOption.SO_SNDBUF, 32*1024)	//设置发送缓冲大小
                .option(ChannelOption.SO_RCVBUF, 32*1024);	//这是接收缓冲大小


        ChannelFuture f = b.bind(8765).sync();              // 异步绑定端口进行监听
        ChannelFuture f2 = b.bind(8764).sync();

        f.channel().closeFuture().sync();                   // 等待关闭 类似Thread.sleep(Integer.MAX_VALUE),让主程序不要关闭
        // 可同时监听两个端口,分别接收数据.接收数据能力增强,但是处理数据能力保持不变,因为serverHandler就一个
        f2.channel().closeFuture().sync();

        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }
}
